package ru.hh.team02.chat.server.SendMessage.sendingBlockingQueue;

import java.util.ArrayList;
import java.util.Set;

import ru.hh.team02.chat.server.Client;
import ru.hh.team02.chat.server.SendMessage.Message;
import ru.hh.team02.chat.server.client.ClientRegistry;

public class SendingBlockingQueue {
	private BlockingQueue<Message> blockingQueue = new SimpleBlockingQueue<Message>();

	/**
	 * Create a new SendingBlockingQueue object
	 * 
	 * @param numberOfThreads
	 *            Number of sending threads.
	 */
	public SendingBlockingQueue(int numberOfThreads) {
		// Creating sending threads and starting them
		for (int i = 0; i < numberOfThreads; i++) {
			Thread sender = new Thread(new Sender());
			sender.start();
		}
	}

	class Sender implements Runnable {
		public void run() {
			while (true) {
				try {
					// Get hold of a message from a queue
					Message message = getMessage();

					// Send the message in this thread
					message.SendMessage();
				} catch (InterruptedException e) {
					// This shouldn't happen as there is nobody to terminate threads
					// according to the contract.
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * Get a message from the queue.
	 * 
	 * @return message
	 * @throws InterruptedException
	 *             if queue is empty and in terminating mode.
	 */
	private Message getMessage() throws InterruptedException {
		return blockingQueue.get();
	}

	/**
	 * Add message to the queue
	 * 
	 * @param message
	 *            message to add to the queue for sending
	 * @exception IllegalStateException
	 *                Thrown when the queue is closed
	 */
	public void add(Message message) {
		blockingQueue.add(message);
	}

	/**
	 * Create a bunch of messages to all the clients of the room and put the
	 * messages in the queue for sending to them. This method is just for
	 * optimization, as we may block the queue only once and add all the
	 * messages instead of blocking and unblocking the queue for each message.
	 * 
	 * @param message
	 *            message to add to the queue for sending
	 * @param room
	 *            Room of clients the message is addressed to
	 * @exception IllegalStateException
	 *                Thrown when the queue is closed
	 */
	public void add(String text, String room) {
		Set<Client> recepients = ClientRegistry.getClientsFromRoom(room);

		// Prepare the array of messages before blocking the queue
		// to block the queue for only time needed to add these messages without
		// waiting for their generation.
		ArrayList<Message> messages = new ArrayList<Message>(recepients.size());
		for (Client recepient : recepients) {
			messages.add(new Message(text, recepient));
		}

		blockingQueue.addAll(messages);
		blockingQueue.notifyAll();
	}
}
